//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2023-01-12     ZhangYihua   first version
//
// Description  : patrol spram memory space periodically and fix soft error data by writing back.
//################################################################################

// soft error fixing for spram
module sef_sp #(
parameter           DEPTH                   = 8192,
parameter           DATA_BW                 = 18,
parameter           RDPL_NUM                = 2,        // Read Data Pipe Line number, MUST >=1

// the following parameters are calculated automatically
parameter           ADDR_BW                 = $clog2(DEPTH)
) ( 
input                                       rst_n,
input                                       clk,

input                                       prd_pls,    // periodical pulse with longer period for patroling

// user side: orignal spram read/write signals
input                                       org_wen,
input                                       org_ren,
input               [ADDR_BW-1:0]           org_addr,
input               [DATA_BW-1:0]           org_wdat,
output                                      org_rvld,
output              [DATA_BW-1:0]           org_rdat,

// memory side: mux patrol, fix and orignal signals
output                                      mux_wen,
output                                      mux_ren,
output              [ADDR_BW-1:0]           mux_addr,
output              [DATA_BW-1:0]           mux_wdat,
input               [DATA_BW-1:0]           mux_rdat,
input                                       mux_rfix,   // detect error and request fixing, it's from ECC module

input                                       cfg_ptrl_ena,   // patroling could be disabled if normal running covers all memory space
input                                       cfg_fix_ena,
input                                       cfg_mem_ena,    // if need mem content uncorrupted during restart, software set it to 1'b0 before restart.
                                                            // if rewriting mem will be always done after restart, tie it to 1'b1.
output                                      sts_ptrl_inc,
output                                      sts_fix_inc,
output              [ADDR_BW-1:0]           sts_fix_addr
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam  [ADDR_BW-1:0]   LAST_ADDR       = DEPTH-1;

reg                 [ADDR_BW-1:0]           mux_addr_pl[RDPL_NUM:0];
reg                 [RDPL_NUM+1-1:0]        mux_ren_pl;
wire                [RDPL_NUM+1-1:0]        mux_ren_pass;
reg                 [RDPL_NUM+1-1:0]        org_ren_pl;
wire                                        org_en;
reg                                         fix_req;
reg                 [ADDR_BW-1:0]           fix_addr;
reg                 [DATA_BW-1:0]           fix_wdat;
reg                                         ptrl_req;
reg                 [ADDR_BW-1:0]           ptrl_addr;

//################################################################################
// main
//################################################################################

always@(*) begin
    mux_addr_pl[0] = mux_addr;
    mux_ren_pl[0] = mux_ren;
    org_ren_pl[0] = org_ren;
end

assign mux_ren_pass[0] = mux_ren_pl[0];
genvar g;
generate for (g=0; g<RDPL_NUM; g=g+1) begin:G_RDPL

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            mux_addr_pl[g+1] <=`U_DLY {ADDR_BW{1'b0}};
        end else begin
            if (mux_ren_pl[g]==1'b1) begin
                mux_addr_pl[g+1] <=`U_DLY mux_addr_pl[g];
            end
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            mux_ren_pl[g+1] <=`U_DLY 1'b0;
        end else begin
            if (mux_ren_pl[g+:2]!=2'b00) begin
                mux_ren_pl[g+1] <=`U_DLY mux_ren_pass[g];
            end
        end
    end

    // during pipeline writing the same address cancel the next pipeline
    assign mux_ren_pass[g+1] = (mux_ren_pl[g+1]==1'b1 && (org_wen==1'b0 || org_addr!=mux_addr_pl[g+1])) ? 1'b1 : 1'b0;

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            org_ren_pl[g+1] <=`U_DLY 1'b0;
        end else begin
            if (org_ren_pl[g+:2]!=2'b00) begin
                org_ren_pl[g+1] <=`U_DLY org_ren_pl[g];
            end
        end
    end

end endgenerate

assign org_rvld = org_ren_pl[RDPL_NUM];
assign org_rdat = mux_rdat;

assign org_en = org_wen | org_ren;
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        fix_req <=`U_DLY 1'b0;
        fix_addr <=`U_DLY {ADDR_BW{1'b0}};
        fix_wdat <=`U_DLY {DATA_BW{1'b0}};
    end else begin
        if (fix_req==1'b1 && ((org_wen==1'b1 && org_addr==fix_addr) || org_en==1'b0)) begin
            fix_req <=`U_DLY 1'b0;
        end else if (fix_req==1'b0 && cfg_fix_ena==1'b1 && mux_rfix==1'b1 && mux_ren_pass[RDPL_NUM]==1'b1) begin
            fix_req <=`U_DLY 1'b1;
            fix_addr <=`U_DLY mux_addr_pl[RDPL_NUM];
            fix_wdat <=`U_DLY mux_rdat;
        end
    end
end
assign sts_fix_inc  = (~org_en) & fix_req;
assign sts_fix_addr = fix_addr;

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        ptrl_req <=`U_DLY 1'b0;
        ptrl_addr <=`U_DLY {ADDR_BW{1'b0}};
    end else begin
        if (ptrl_req==1'b0 && cfg_ptrl_ena==1'b1 && prd_pls==1'b1) begin
            ptrl_req <=`U_DLY 1'b1;
            ptrl_addr <=`U_DLY (DEPTH==(1<<ADDR_BW) || ptrl_addr<LAST_ADDR) ? ptrl_addr+1'd1 : {ADDR_BW{1'b0}};
        end else if (ptrl_req==1'b1 && ((org_en==1'b0 && fix_req==1'b0) || (org_en==1'b1 && org_addr==ptrl_addr))) begin
            ptrl_req <=`U_DLY 1'b0;
        end
    end
end
assign sts_ptrl_inc = (~org_en) & (~fix_req) & ptrl_req;

assign mux_wen = cfg_mem_ena & (org_wen | (fix_req & (~org_ren)));
assign mux_ren = cfg_mem_ena & (org_ren | (ptrl_req & (~org_wen) & (~fix_req)));
assign mux_addr = (cfg_mem_ena==1'b0) ? {ADDR_BW{1'b0}} :
                  (org_en==1'b0 && fix_req ==1'b1) ? fix_addr  : 
                  (org_en==1'b0 && ptrl_req==1'b1) ? ptrl_addr :
                                                     org_addr;
assign mux_wdat = (org_wen==1'b1) ? org_wdat : fix_wdat;

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
